import {
    _decorator,
    Component,
    EventKeyboard,
    Input,
    input,
    KeyCode,
} from 'cc';
const { ccclass, property } = _decorator;

@ccclass('InputUtil')
export class InputUtil {
    private static instance: InputUtil | null = null;

    static Instance() {
        if (InputUtil.instance == null) {
            InputUtil.instance = new InputUtil();
        }
        return InputUtil.instance;
    }

    fnMap = new Map<KeyCode, VoidFunction>();

    constructor() {
        input.on(Input.EventType.KEY_DOWN, this.onInputEvent, this);
    }

    /**
     *
     * 相同的KeyCode会覆盖
     */
    addEvent(key: KeyCode, call: VoidFunction) {
        this.fnMap.set(key, call);
    }

    onInputEvent(event: EventKeyboard) {
        for (let pair of this.fnMap) {
            const [key, call] = pair;
            if (event.keyCode === key) {
                call();
            }
        }
    }

    /**
     * 注册原生事件,可以多次触发
     */
    on(key: KeyCode, call: VoidFunction) {
        input.on(
            Input.EventType.KEY_DOWN,
            (event: EventKeyboard) => {
                if (event.keyCode === key) {
                    call();
                }
            },
            this,
        );
    }
}

const keyBindingRegistry = new WeakMap<
    any,
    { key: KeyCode; method: string }[]
>();

//方法装饰器
export function KeyBinding(key: KeyCode) {
    return function (target: any, propertyKey: string) {
        if (!keyBindingRegistry.has(target)) {
            keyBindingRegistry.set(target, []);
        }
        keyBindingRegistry.get(target)!.push({ key, method: propertyKey });
    };
}

//类装饰器：自动注册到 InputUtil
export function AutoBindKeys<T extends new (...args: any[]) => Component>(
    constructor: T,
) {
    const originalOnLoad = constructor.prototype.onLoad;

    constructor.prototype.onLoad = function () {
        const bindings = keyBindingRegistry.get(constructor.prototype);
        if (bindings) {
            for (const { key, method } of bindings) {
                const fn = this[method].bind(this);
                InputUtil.Instance().addEvent(key, fn);
            }
        }
        originalOnLoad?.call(this);
    };
}
